package bot;

import gui.BotGui;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * Contains utility methods that don't fit a class, but are none the less needed.
 * @author Jamison Bradley
 */
public class Util {

	/**
	 * Calculates the population density of a city in people per square unit of choice.
	 * @param pop is the population of the city.
	 * @param area is the area of the city in unit of choice.
	 * @return The population density of the city in the unit of choice.
	 */
	public static String calculateDensity(double pop, double area){
//		DecimalFormat df = new DecimalFormat("#.#");
		return Util.format(pop / area, 1, false);
	}
	
	
	/**
	 * Replaces the next number after the starting point with the given String.
	 * @param str is the string that is having a number in it replaced.
	 * @param start is the starting point, as in charAt(start), of the search for the next number.
	 * @param toInsert is the String that is being inserted in place of the next number found.
	 * @return
	 */
	public static String replaceNextNumber(String str, int start, String toInsert){
		//look for the start and end of the number
		int startOfNumber = -1;
		int end = -1;
		for (int j = start; j < str.length(); j++){
			if (startOfNumber == -1 && str.charAt(j) >= '0' && str.charAt(j) <= '9'){
				startOfNumber = j;
			}
			//if the start point has been found the end of the number should be found next
			if (startOfNumber >= 0 && !((str.charAt(j) >= '0' && str.charAt(j) <= '9') || str.charAt(j) == '.')){
				end = j;
				break;
			}
		}
		str = str.substring(0, startOfNumber) + toInsert + str.substring(end, str.length());
		return str;
	}
	
	/**
	 * Converts from square miles to squre kilometers.
	 * @param sqMiles is the square miles value being converted to square kilometers.
	 * @return The value of the input in square kilometers.
	 */
	public static double convertToSqKm(double sqMiles){
		//multipy sq miles by the conversion factor given by Google calculator
		return sqMiles * 2.58998811;
	}
	
	/**
	 * Sets the paste buffer of the System to the String that is provided.
	 * @param str to be put into the paste buffer.
	 */
	public static void setPasteBuffer(String str){
		Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
		Util util = new Util();
		MyTransfer transfer = util.new MyTransfer(str);
		clip.setContents(transfer, null);
	}
	
	/**
	 * Gets the information in the System's copy buffer and returns it as a String if it is a String.
	 * @return The text that is in the System's copy buffer if it is text.
	 */
	public static String getCopyBuffer(){
		String ret = "";
		Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
		try {
			if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)){
				ret = (String) t.getTransferData(DataFlavor.stringFlavor);
			}
//			ret = (String) clip.getData(DataFlavor.stringFlavor);
		} 
		catch (UnsupportedFlavorException e){
			e.printStackTrace();
		} 
		catch (IOException e){
			e.printStackTrace();
		}
		return ret;
	}
	
	/**
	 * Removes all commas from the given String.
	 * @param str is the String whose comma's should be removed.
	 * @return The original String minus any commmas that might be present in it.
	 */
	public static String removeCommas(String str){
		for (int i = 0; i < str.length(); i++){
			//if a comma is found remove it
			if (str.charAt(i) == ','){
				//comma not the last char in the String
				if (i < str.length() - 1){
					str = str.substring(0, i) + str.substring(i + 1, str.length());
				}
				//comma is the last char in the String
				else {
					str = str.substring(0, i);
				}
			}
		}
		return str;
	}
	
	/**
	 * Rounds the given value to a certain decimal digit of precision.
	 * @param value that is being rounded.
	 * @param decimalDigitsKept the number of decimal digits that is being kept ex: 3.14 keeping 1 digit returns 3.1
	 * @return
	 */
	public static double round(double value, int decimalDigitsKept){
		double multiplier = Math.pow(10, decimalDigitsKept);
		return Math.round(value * multiplier) / multiplier;
	}
	
	/**
	 * Formats a double value into a String with a given number of digits after the decimal.
	 * @param value that is being formated, will be rounded.
	 * @param decimalDigitsPrecision number of digits after the decimal point that are to be displayed.
	 * @param zeroWithDecimal if false 0 will always be returned as 0 with no 0's after the decimal if true 0's will be added after decimal to
	 * have the proper number of digits after decimal.
	 */
	public static String format(double value, int decimalDigitsPrecision, boolean zeroWithDecimal){
		String ret = "" + round(value, decimalDigitsPrecision);
		for (int i = 0; i < ret.length(); i++){
			if (ret.charAt(i) == '.'){
				int digits = ret.length() - i - 1;
				if (digits < decimalDigitsPrecision){
					for (int j = 0; j < decimalDigitsPrecision - digits; j++){
						ret += "0";
					}
				}
			}
		}
		if (!zeroWithDecimal){
			String comparison = "0.";
			for (int i = 0; i < decimalDigitsPrecision; i++){
				comparison += "0";
			}
			if (ret.equals(comparison)){
				ret = "0";
			}
		}
		return ret;
	}
	
	/**
	 * Parses the name of a city out a Wikipedia url to a city page edit.
	 * @param wikiUrl is the url of the city being edited.
	 * @return The name of the city.
	 */
	public static String parseName(String wikiUrl){
		String ret = "";
		int start = 0;
		int end = 0;
		//find the beginning of the name title= precedes it
		for (int i = 0; i < wikiUrl.length() - 5; i++){
			if (wikiUrl.substring(i, i + 5).equals("title")){
				start = i + 6;
				//find the end of the name
				for (int j = i +7; j < wikiUrl.length(); j++){
					if (wikiUrl.charAt(j) == ','){
						end = j;
						break;
					}
				}
				ret = wikiUrl.substring(start, end);
				//change any underscores to spaces
				ret = ret.replace('_', ' ');
				break;
			}
		}
		//replace %27 with '
		String[] test = ret.split("%27");
		if (test.length > 1){
			ret = test[0] + "'" + test[1];
		}
		return ret;
	}
	
	/**
	 * Cuts off the bottom of the Wikipedia edit source since there are strange chars down there that don't copy correctly.
	 * @param source is the page source of the Wikipedia page.
	 * @return A Wikipedia page source with the code below the references section cut out.
	 */
	public static String cropSource(String source){
		String ret = "";
		//find the references section and only take the text before that
		for (int i = 0; i < source.length(); i++){
			if (source.charAt(i) == '='){
				//check to see if it is the references section
				if (source.substring(i, i + 14).equals("==References==")){
					ret = new String(source.substring(0, i + 14));
					break;
				}
				else if (source.substring(i, i + 16).equals("== References ==")){
					ret = new String(source.substring(0, i + 16));
				}
			}
		}
		
		return ret;
	}
	
	/**
	 * Converts a String in the form /wiki/cityName,_State to the url that will take you to its edit page
	 * @param str that will be converted to the edit page url.
	 * @return String representing the url that can be used to access the edit page for the given city.
	 */
	public static String convertToEditUrl(String str){
		String firstHalf = "http://en.wikipedia.org/w/index.php?title=";
		String thirdHalf = "&action=edit";
		return firstHalf + str.split("/")[2] + thirdHalf;
	}
	
	/**
	 * Converts a city name to a wikipedia edit url
	 * @param str city name
	 * @return String representing the url that can be used to access the edit page for the given city.
	 */
	public static String convertNameToEditUrl(String str){
		String temp = str.replaceAll(" ", "_");
		return convertToEditUrl("/wiki/" + temp + ",_" + BotGui.state);
	}
	
	/**
	 * Gets the last entry in the from file and moves it to the last location in the to file.
	 * @param from is the file the line is retrieved from.
	 * @param to is the file the line is placed in.
	 * @return the line that was taken from the end of the from file.
	 */
	public static String getLastEntry(File from, File to){
		List<String> cities = new ArrayList<String>();
		String ret = null;
		try{
			BufferedReader input = new BufferedReader(new FileReader(from));
			String line = input.readLine();
			while(line != null){
				cities.add(line.split("~")[0]);
				line = input.readLine();
			}
			if (cities.size() != 0){
				ret = cities.get(cities.size() - 1);
			}
			else{
				return ret;
			}
			//rewrite from file without last city at the end
			FileOutputStream writer = new FileOutputStream(from);
			cities.remove(cities.size() - 1);
			String output = "";
			for (String str: cities){
				output += str + "\n";
			}
			writer.write(output.getBytes());
			//add the last city to the end of to file
			String content = "";
			input = new BufferedReader(new FileReader(to));
			line = input.readLine();
			while (line != null){
				content += line + "\n";
				line = input.readLine();
			}
			content += ret;
			writer = new FileOutputStream(to);
			writer.write(content.getBytes());
			writer.close();
		} 
		catch (FileNotFoundException e){
			e.printStackTrace();
		} 
		catch (IOException e){
			e.printStackTrace();
		}
		return ret;
	}
	
	/**
	 * Removes all forward slasheds (\) from a given String.
	 */
	public static String removeForwardSlashes(String str){
		String[] parts = str.split("\\\\");
		StringBuilder b = new StringBuilder(100);
		for (String part: parts){
			b.append(part);
		}
		return b.toString();
	}
	
	/**
	 * Retrieves the part of a string that gets removed with the split method is used on the String.
	 * @param complete the entire string before the split
	 * @param partOne the first part of the split return array
	 * @param partTwo the second part of the split return array
	 * @return
	 */
	public static String retrieveMissingPiece(String complete, String partOne, String partTwo){
		return complete.substring(partOne.length(), complete.length() - partTwo.length());
	}
	
	public static void addCityLinkToFile(City city, File file){
		try{
			String formattedInput = "/wiki/" + city.getName() + ",_" + BotGui.state;
			String toWrite = Util.convertToEditUrl(formattedInput);
			BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
			writer.write(toWrite + "\n");
			writer.close();
		} 
		catch (FileNotFoundException e){
			e.printStackTrace();
		} 
		catch (IOException e){
			e.printStackTrace();
		}
	}
	
	public static void addRemovedTextAndLinkToFile(City city, String text, File file){
		try{
			String formattedInput = "/wiki/" + city.getName() + ",_" + BotGui.state;
			String toWrite = Util.convertToEditUrl(formattedInput);
			BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
			writer.write(text);
			writer.write("\n");
			writer.write(toWrite);
			writer.write("\n");
			writer.close();
		}
		catch (FileNotFoundException e){
			e.printStackTrace();
		} 
		catch (IOException e){
			e.printStackTrace();
		}
	}
	
	/**
	 * Takes a normal number and converts it to a spelled number if it is < 10
	 * @return The number written out if it is < 10 other wise the input is returned.
	 */
	public static String smallNumberCheck(String number){
		String numbers[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
		String written[] = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
		for (int i = 0; i < numbers.length; i++){
			if (number.equals(numbers[i])){
				return written[i];
			}
		}
		return number;
	}
	
	/**
	 * Finds the infobox section of a Wikipedia article and returns it as a String.
	 * @param source of the Wikipedia page whose infobox section is desired.
	 * @return The String representing the infobox of the Wikipedia page.
	 */
	public static String getInfobox(String source) throws IllegalArgumentException {
		String test[] = source.split("\\{\\{[I|i]nfobox *?[S|s]ettlement");
		if (test.length != 2){
			throw new IllegalArgumentException("does not have an infobox of type settlement");
		}
		String article = Util.retrieveMissingPiece(source, test[0], test[1]) + test[1];
		
		int location = article.length() - 1;
		int closings = 0;
		for (int i = article.length() - 2; i >= 0; i--){
			if (article.substring(i, i + 2).equals("}}")){
				if (closings == 0){
					location = i;
				}
				closings++;
			}
			else if (article.substring(i, i + 2).equals("{{")){
				closings--;
			}
		}
		String infobox = article.substring(0, location);
		return infobox;
	}
	
	public static String removeReferences(String source){
		String openings[] = source.split("<ref");
		StringBuilder builder = new StringBuilder(1000);
		builder.append(openings[0]);
		for (int i = 1; i < openings.length; i++){
			String test[] = openings[i].split("</ref");
			if (test.length == 2){
				builder.append(test[1]);
			}
		}
		return builder.toString();
	}
	
	public static void addTextToFile(String text, File f){
		try{
			BufferedWriter writer = new BufferedWriter(new FileWriter(f, true));
			writer.write(text);
			writer.write("\n");
			writer.close();
		} 
		catch (IOException e){
			e.printStackTrace();
		}
	}
	
	/**
	 * Constructs a Transferable Object with a String.
	 * @author Jamison Bradley
	 */
	private class MyTransfer implements Transferable{
		/**
		 * The string that will be transfered to the System's paste buffer
		 */
		String contents;
		
		/**
		 * Constructs the MyTransfer object.
		 * @param s
		 */
		public MyTransfer(String s){
			contents = s;
		}
		
		@Override
		public Object getTransferData(DataFlavor arg0) throws UnsupportedFlavorException, IOException {
			return contents;
		}

		@Override
		public DataFlavor[] getTransferDataFlavors() {
			DataFlavor[] flavors = new DataFlavor[1];
			flavors[0] = DataFlavor.stringFlavor;
			return flavors;
		}

		@Override
		public boolean isDataFlavorSupported(DataFlavor arg0) {
			return arg0.isFlavorTextType();
		}
	}
	
}


